Chapter 1:
Introduction

We've been working with GUI components for a couple of lessons now. It's time to see how to put them together into a complete, working GUI application. For the next two lessons, we're going to pretend we run a small pizza shop. We want to build a GUI Java application that will allow customers to order a pizza online. When they're done creating the order, we'll allow them to save it so we can make the pizza they ordered.

We're going to simplify the application quite a bit in order to make it something we can do in two lessons, so you'll need to suspend your disbelief long enough to pretend this application does everything a real pizza ordering program would do.

While we're working through this project, we'll also pick up a few more GUI components to add to our inventory. These include radio buttons, check boxes, text fields, borders, and a few more odds and ends.

When we're done, this is what our program will produce:

The GUI Pizza application

The GUI Pizza application

We've got quite a bit to do, so let's get to it!

Chapter 2:
Getting Set Up

I imagine that the thought running through your head right now is, "Where do I start?" If that's the case, I know how you feel. I've been there a few times myself. So here's where we'll start: Take our finished program from the previous lesson and remove everything from the content pane. Then modify the menus so they include only the following options:

For the File menu, include three options: New Order, Save Order, and Exit. That menu will look something like this:

The File menu

The File menu

Our Help menu will have only the About option, and it will look like this:

The Help menu

The Help menu

When clicked, the About GUI Pizza item should produce a dialog box that looks something like this:

The About dialog box

The About dialog box

Since these are all things we've done before, I'll let you get that much set up before we continue. If you have trouble getting it to work right, let me know in the Discussion Area, or take a peek at my code here.

Getting Set Up Solution

If you looked at my code, you probably noticed that I added a few more methods than last time to organize my code hierarchically and to keep my methods small. So now my start() method calls makeMenus(), and makeMenus() calls makeFileMenu() and makeHelpMenu(). We'll continue this type of pattern as we go on because it makes the program easier to follow once you get used to it.

Our last setup task is to add variables to the program for each of the buttons, check boxes, and text fields we'll use. We'll do this now because we want to have them available to the code in more than one of our methods. We're also going to take a different approach to using these buttons and fields than we did in previous lessons. We won't assign an ActionListener to each one, even though we can.

Why not? We don't really want our program to jump in and take action every time a user clicks or changes one of these buttons or fields. After all, users may change their minds several times about which crust they want, and it doesn't matter to us until they save the order.

Instead of listening to every change of every item, then, our program will wait until the user clicks the Save Order option in the File menu. Then the program will look and see which items have information for us and which don't.

To make that happen, we're going to add an instance variable to our program for each button, box, and text field on the screen. This way, we can use them whenever we need them. I've listed the variables we'll need below in their declarations. Their names and types should make it clear which variable corresponds to which item on the screen. We'll add them at the start of the class, right after our JFrame declaration.

    // radio buttons and button group
private JRadioButton regularCrustButton;
private JRadioButton thinCrustButton;
private JRadioButton handCrustButton;
private JRadioButton deepCrustButton;
private ButtonGroup crustButtonGroup;

// check boxes private JCheckBox pepperoniBox; private JCheckBox sausageBox; private JCheckBox cheeseBox; private JCheckBox pepperBox; private JCheckBox onionBox; private JCheckBox mushroomBox; private JCheckBox oliveBox; private JCheckBox anchovyBox;

// text fields private JTextField breadSticksText; private JTextField buffaloWingsText; private JTextField nameText; private JTextField addressText; private JTextField cityText;

Designing Our Window

Now that we can produce a window with menus, where do we go from here?

Let's start by taking a good look at the window we want to create. What elements does it have? How are they laid out? Does their layout match up well with any of the layout managers we learned about in Lesson 6?

I hope you answered "Yes!" to the last question. These elements do match up well with one of the layout managers we studied. "Which one?" you might ask.

Do you remember the BorderLayout manager? Remember how it divided a window? It was like this:

Java's BorderLayout

Java's BorderLayout

If we put the pizza picture in the north region, the crust choices in the west, toppings in the center, side orders in the east, and the address entry in the south, then things line up pretty well, don't they? That's how we're going to approach our window.

To get ready to add the components, we need to initiate our frame's layout manager. If you look in the start() method, you'll see that we already have a call to makeMenus() to set up our menus. But we haven't done anything in our content pane yet. Let's initiate the content pane and get the layout manager laid out, so to speak.

To keep with our program's organization scheme, we're going to add a call in the start() method, right after the makeMenus() call. It will call a method that sets up our content pane, so let's call it makeContent(). That one-line insertion looks like this:

    makeContent();
    
    

Of course, to make the call work, we have to add a new method with that name. That method is where we'll do the work to set up the body of our window. Right after the last of the menu methods, let's add a method that looks like this:

    private void makeContent()
{
}
	
    

The method doesn't do anything yet, but just hang in there—we'll have it working in no time. (Well, maybe in a little while.)

We'll need a content pane in this method, too. Since we'll be working with this pane in more than one method, let's make it easy to use by declaring it at the top of our class, right after our frame. We will also need to use methods that don't belong to the Container class, but to the JPanel class. So we'll declare the variable as a JPanel object, like this:

    private JPanel contentPane;
    
    

Now we're ready to go to work on our content. Here's how we get the pane and link it to our layout manager:

    contentPane = (JPanel)frame.getContentPane();
contentPane.setLayout(new BorderLayout(6,6));
	
    

Note the addition of "(JPanel)" in front of the call to getContentPane(). That is called "casting" in Java, and it's telling Java that even though the getContentPane() method says it's returning a Container object to maintain its backward compatibility, we actually know it's a JPanel object and want Java to treat it like one.

There's one difference between this layout constructor and the one we used in the last lesson. The two numbers in the constructor argument list tell it that I want 6 pixels both horizontally and vertically between components in my window. If you look at the window again in the first figure up above, you'll see that there is a little spacing between the different groups of components. It looks better that way, and I wanted to introduce this aspect of the layout manager here.

Now we have the window and its layout, and we already have the menu bar set up. We're finally ready to start adding content.

Let's start at the top with the picture!

Chapter 3:
Loading an Image

The first thing at the top of our screen is a picture of a pizza. Let's see how to load and display that image. Here's the image I used:

Pizza!

Pizza!

If you have another image you like better, feel free to use it. To use this one, just right-click it and save the image to your program folder. Its name here is L08-06.jpg, but you can rename it if you want a more descriptive name. If you change it, just be sure to use the new name in the program.

One more organizational note before we get the image (last one, I promise!): In order to keep our makeContent() method from getting too big, we're going to make a separate method for each of the five layout regions, which we'll call from makeContent(). Let's make the names simple to remember: makeNorthRegion(), make SouthRegion(), and so on. Since we're getting ready to work on the north region, let's add a call to its method in makeContent():

    makeNorthRegion();
    
    

Right after makeContent(), add the new method:

    private void makeNorthRegion()
{
}
	
    

Now, since we're only putting one item, an image, into the north region, we don't need to use a layout manager in that region. We can add the image directly to the content pane.

Java has several ways to load and display images. Since we're not going to do anything fancy with this image, I'm going to show you the easiest way I know. It's almost a one-liner. Here's all we need to add to makeNorthRegion():

    JLabel imgLabel = new JLabel(new ImageIcon("L08-06.jpg"), JLabel.CENTER);
contentPane.add(imgLabel, BorderLayout.NORTH);
	
    

TQA-22 --- It turns out that Java labels (JLabel objects) are pretty flexible and can hold not only text but also images in the form of icons (ImageIcon objects). It also comes in handy that the icons in Java are flexible and can hold almost any image. The first line above creates a new label named imgLabel, and instead of putting text into the label, it creates an ImageIcon by giving it the file name of the image we want to load. If the image were in another directory, we could still load it by using its full path in place of its name above. The last parameter in the first line tells Java that we want to center the image in the label.

Once the label has the image, we just have to add it to the content pane's north region. That's what the second line does. If you run the program now, you'll see this:

Pizza in a window

Pizza in a window

If you see something else, let me know in the Discussion Area and I'll help you fix it.

Radio Buttons

That takes care of the north region, so let's move on to the west region, which will contain radio buttons to select the pizza crust. Radio buttons are small buttons, usually round, that allow you to choose one option from a group. They get their name from the way buttons on a radio work, selecting one station and turning off all the rest. Clicking a radio button selects (or turns on) that option and deselects (turns off) all other buttons in the group.

In addition to the buttons themselves, we'll need a group to tell Java what buttons go together. We will also put a border around them that will serve two purposes: It will provide a visual boundary to separate these buttons from the rest of the window, and it will allow us to add a title that describes what's inside the border.

Since all of these components need to go into a region that only accepts one item, we have to create a panel to hold everything. And that panel will need a layout to organize its components. We want the buttons to line up vertically, so we'll use a BoxLayout with a vertical axis.

That's a lot to take in, so let's go through it one step at a time.

The first thing we'll add to our code is a call to the method that will build the western region. That call goes into our makeContent() method, right after the call to makeNorthRegion(), and it looks like this:

    makeWestRegion();
    
    

Next, we'll add the new empty method to our program:

    private void makeWestRegion()
{
}
	
    

The first two lines of the method will create a panel to work in and assign a layout manager to it. We've done this before, so I won't explain again in detail. This is what these lines look like:

    JPanel panel = new JPanel();
panel.setLayout(new BoxLayout(panel,BoxLayout.Y_AXIS));
	
    

TQA-24 --- Next, we'll create a border for the panel. Borders are very helpful in organizing a window visually, and Java makes them easy to create. There's a Java class called BorderFactory whose only purpose is to create borders for us.

We can create several types of borders, including etched borders, beveled borders, empty borders, line borders, and titled borders. There are even more, but we won't get into them here. Sun has an excellent tutorial on borders, and I've included a link to it in the Supplementary Material section at the end of this lesson. If you want to know more about borders, check it out.

We're going to use a basic titled border with its default settings, which include a line and a title. Here's the Java statement that creates it for us:

    panel.setBorder(BorderFactory.createTitledBorder("Choose a Crust"));
    
    

Every Swing component has a setBorder() method, so it's easy to establish the border for our panel. We use BorderFactory's createTitledBorder() method to create the border we want and give it the text to display. Creating a border is that easy.

The last thing to do before we start creating the buttons is to create the button group we want to add them to. The group tells Java what buttons are related so that Java knows which ones to turn off when a user clicks another button in the group. Creating a button group is also easy:

    crustButtonGroup = new ButtonGroup();
    
    

Now we're finally ready to create our radio buttons. As we make each one, we'll add it to both the group and the panel like this:

    regularCrustButton = new JRadioButton("Regular Crust", true);
crustButtonGroup.add(regularCrustButton);
panel.add(regularCrustButton);
	
    

The first line creates the radio button and gives it the text to display. It also tells Java whether the button should be selected or not. If the second argument is true, the button will be selected when it is created. If the argument is false, it will not. Remember that only one radio button can be selected, and in this case, it will be the first one. All the other radio buttons will need to have an argument of false.

The second line above adds the button to the button group, and the third line adds it to the panel.

Using that as an example, I'll let you write the code for the other three radio buttons on your own. When you finish those, there's one more line to put at the end of the method: the line that adds the panel to the west region of our content pane. That line is similar to the one we used to add the image of the pizza to the north region, so I'll let you add that one yourself, too.

When you're done, you can compare what you've written to my version of the makeWestRegion() method here.

Latest version of the makeWestRegion() method here.

Try running your program now. It should look something like this:

Window with radio buttons

Window with radio buttons

If you see something else, let me know and we'll compare notes.

Chapter 4:
Check Boxes

The next region we'll do is the center region, with the toppings check boxes in it. It's similar to the west region and even a little simpler, so it should go quicker.

Once again, we'll add a new method to build it, this time named makeCenterRegion(). The start of the method is similar to our makeWestRegion() method: It creates a panel, gives it a vertical BoxLayout, and creates a border (this time with the title "Select Toppings"). We don't need a button group since check boxes don't have any effect on each other—depending on their preferences, users can select all, some, or none of them.

The following lines create one of our check boxes and add it to the panel:

    pepperoniBox = new JCheckBox("Pepperoni", false);
panel.add(pepperoniBox);
	
    

There are seven more check boxes, and I'll let you add them yourself for practice. And don't forget to add the panel to the center region of the content pane before you end the method. If you want to check your code against mine, you can click here.

Latest version of the makeCenterRegion() method here.

Here's what our window looks like after adding the center region:

Window with check boxes

Window with check boxes

You can see that the west region's height expanded to match the center's, and that the center region's width adjusted to match the north region's width. Next, we'll add the east region, which has a couple of text entry fields.

Text Fields

TQA-23 --- Looking back at our finished window, we see that the east region has two text fields. Text fields are GUI components that allow a user to enter text. The two in this region let users enter the number of side orders they want. Each text field has a label next to it so we know what a number in that field means. Unlike radio buttons and check boxes, text fields don't have automatic text labels associated with them, so we'll have to generate the text fields and labels separately and then put them together on a line.

Let's go ahead and add our makeEastRegion() method and its call in makeContent() so we have a starting point. We'll also set up the panel, its layout (BoxLayout with a vertical axis again), and its border. Our method looks like this:

    private void makeEastRegion()
{
    // set up side orders with quantities in EAST
    JPanel panel = new JPanel();
    panel.setLayout(new BoxLayout(panel,BoxLayout.Y_AXIS));
    panel.setBorder(BorderFactory.createTitledBorder("Sides (Enter Quantity)"));
}
	
    

Here's where things get a little more interesting. We want each line of our panel in this method to hold a text field and a label side by side. To do that, we have to introduce one more layer—another panel with its own layout. Let's create one more panel named smallPanel, and we'll give it a BoxLayout with a horizontal axis. Here's how to do that:

    JPanel smallPanel = new JPanel();
smallPanel.setLayout(new BoxLayout(smallPanel,BoxLayout.X_AXIS));
	
    

Now we're ready to create our text field and label. After that, we'll do three things: add the text field and label to the small panel, add the small panel to the larger panel, and add the larger panel to the content pane.

Here's the code that will do that for the first line:

    breadSticksText = new JTextField("",2);
smallPanel.add(breadSticksText);
smallPanel.add(new JLabel(" Bread Sticks"));
panel.add(smallPanel);
contentPane.add(panel, BorderLayout.EAST);
	
    

The first line above creates a new text field, a JTextField object, with no text in it and a minimum width of two columns. This is where customers will enter how many orders of bread sticks they want. The second line adds the text field to our smallPanel. The third line creates a label to go alongside the text field, and it adds the label to smallPanel. The fourth line adds smallPanel to panel, and the last line adds panel to contentPane.

If you add this code to our program so far and run it, the east region of our window will look rather strange:

Window with a tall text box

Window with a tall text box

There are a couple of problems here that we're going to fix. The first is obvious: The text box is too tall. By default, text boxes will fill as much space as you give them. We set the text box width to two columns, but we didn't set its height, so it automatically became as tall as our other two regions.

The second problem is not quite so obvious: The panel isn't wide enough for our border title. Remember, the east and west regions have their widths set by the widths of the items in them. And so far, our items are not wide enough for our title.

Let's fix the width of our panel first. We can set a panel (and many other components) to a preferred size that some layout managers will honor. Here's how we'll set ours:

    panel.setPreferredSize(new Dimension(150,0));
    
    

This statement can go just about anywhere in our method. Let's put it on the line after we set the border—the fourth line of the method. That keeps the panel's setup statements together. The method we called, setPreferredSize(), uses one argument, and it has to be a Dimension object. Dimension is a Java class designed to hold two values: another object's width and height. We set our panel's width to 150 pixels, which is wide enough to display our title. We set its height to 0, since as long as it is less than the height of our center region, it won't make a difference. Remember, the tallest region of a BorderLayout's east, center, and west regions controls the height of all three.

Now let's fix the height of our text field. Just like we set the panel's preferred size, we can set a component's maximum size:

    breadSticksText.setMaximumSize(new Dimension(20,24));
    
    

We set the size of the text field to a maximum width of 20 pixels and a maximum height of 24 pixels, which is about the size we want. If you add that line and run the program, you'll see one more little problem. Our small panel is centered horizontally in our larger panel, but we want it to be at the left side of the panel. We can fix that with one more adjustment:

    smallPanel.setAlignmentX(Component.LEFT_ALIGNMENT);
    
    

That finally takes care of our first line in the east region. I told you it would be interesting!

Go ahead and add the second item to the panel by using the first as a model. If you have trouble with it, you can look at my version of the method here.

Latest version of the makeEastRegion() method here.

My version of the makeEastRegion() method here.

If you put everything together and run the program now, you should see this window:

Window with text fields

Window with text fields

Chapter 5:
Summary

As you might have noticed, we're not quite done creating our window yet, but we've covered enough ground for one lesson!

We now have a working window with menus, a picture, radio buttons, check boxes, and text fields with labels, all nicely organized and laid out.

In the next lesson, we'll finish up the south region of the window, and then we'll add the code we need to make the menu options work.

I'll see you next time.


Lesson 8 FAQs

Q: Are there any automated ways to generate a window layout in Java, or do we have to lay the components out by hand every time?

A: There is an automated way to create a window in Java. The NetBeans IDE lets you create a form that represents your window. Then you drag and drop components into it and place them where you want them. NetBeans will generate Java code that will display the window you've designed.

The reason we did not start out using a drag-and-drop process is that once you have your components in place, you still have to modify the code to make them work. Jumping into the middle of that code without understanding what it does can be dangerous. Once we've finished doing it "the hard way," you'll be able to look at code generated by an IDE like NetBeans and figure out how it works. Then you'll be able to modify it appropriately.


Lesson 8 Assignment

Today, I'd like you to design a simple application of your own using the tools we've worked with in the last few lessons. Include at least one menu, an image, one of the button types we've worked with so far (JButton, JRadioButton, or JCheckBox), and perhaps a text field. Design the window and put the components into it so that it displays however you want it to. Don't worry about making the components do any real work yet. We'll concentrate on that in the next lesson. Just get it looking the way you want for this lesson.

Let me know what problems or questions you run into on this assignment. I'll see you in the Discussion Area.

Lesson 8 Quiz Answers

1. How do button groups and radio buttons work together?
The button group controls which radio buttons Java turns off when a user clicks one of the buttons in the group to turn it on.

2. What does a Java border do for us?
It visually separates areas in the window.

3. How does a group of radio buttons work?
Only one radio button in a group can be selected at any given time.

4. How does a group of check boxes work?
Check boxes are independent of each other, and you can check any number of them at a time.

5. What does the Dimension class do for us?
It stores a component's width and height in a single object.


Lesson 8 Supplementary Materials